package net.huashitong.ssydt.provider.exam.service;

import com.alibaba.fastjson.JSON;
import net.huashitong.ssydt.article.entity.SsArticleArticle;
import net.huashitong.ssydt.exam.constant.ExamConsts;
import net.huashitong.ssydt.exam.entity.*;
import net.huashitong.ssydt.exam.entity.vo.*;
import net.huashitong.ssydt.exceptions.ServiceException;
import net.huashitong.ssydt.provider.article.service.SsArticleArticleService;
import net.huashitong.ssydt.provider.money.service.MoneyService;
import net.huashitong.ssydt.provider.sys.service.SsSysBasService;
import net.huashitong.ssydt.provider.task.service.SsTaskTaskRecordService;
import net.huashitong.ssydt.provider.utils.StringUtils;
import net.huashitong.ssydt.provider.utils.date.DateUtils;
import net.huashitong.ssydt.provider.web.model.Result;
import net.huashitong.ssydt.sys.constant.SysBasConsts;
import net.huashitong.ssydt.sys.entity.SsSysBas;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.regex.Pattern;

/**
 * @author <a href="mailto:yhy23456@163.com">huiyang.yu</a>
 * @since 2017.08.23
 */
@Service
@Transactional
public class ExamServiceImpl implements ExamService {

    private SsExamInfoService ssExamInfoService;

    private SsSysBasService ssSysBasService;

    private SsExamTestScheduleService ssExamTestScheduleService;

    private SsExamTestRecordService ssExamTestRecordService;

    private SsExamQuestionService ssExamQuestionService;

    private MoneyService moneyService;

    private SsExamTestQuestionService ssExamTestQuestionService;

    private SsExamQuestionOptionService ssExamQuestionOptionService;

    private SsExamPaperService ssExamPaperService;

    private SsExamPaperRecordService ssExamPaperRecordService;

    private SsExamPaperQuestionService ssExamPaperQuestionService;

    private SsExamPaperQuestionRecordService ssExamPaperQuestionRecordService;

    private SsExamErrorQuestionService ssExamErrorQuestionService;

    private SsExamErrorRecordService ssExamErrorRecordService;

    private SsExamCollectionQuestionService ssExamCollectionQuestionService;

    private SsExamPaperTypeHotService ssExamPaperTypeHotService;

    private SsArticleArticleService ssArticleArticleService;

    private SsExamArticleRecordService ssExamArticleRecordService;

    private SsExamArticleQuestionRecordService ssExamArticleQuestionRecordService;

    private SsTaskTaskRecordService ssTaskTaskRecordService;

    @Autowired
    public ExamServiceImpl(SsExamInfoService ssExamInfoService,
                           SsSysBasService ssSysBasService,
                           SsExamTestScheduleService ssExamTestScheduleService,
                           SsExamTestRecordService ssExamTestRecordService,
                           SsExamQuestionService ssExamQuestionService,
                           MoneyService moneyService,
                           SsExamTestQuestionService ssExamTestQuestionService,
                           SsExamQuestionOptionService ssExamQuestionOptionService,
                           SsExamPaperService ssExamPaperService,
                           SsExamPaperRecordService ssExamPaperRecordService,
                           SsExamPaperQuestionService ssExamPaperQuestionService,
                           SsExamPaperQuestionRecordService ssExamPaperQuestionRecordService,
                           SsExamErrorQuestionService ssExamErrorQuestionService,
                           SsExamErrorRecordService ssExamErrorRecordService,
                           SsExamCollectionQuestionService ssExamCollectionQuestionService,
                           SsExamPaperTypeHotService ssExamPaperTypeHotService,
                           SsArticleArticleService ssArticleArticleService,
                           SsExamArticleRecordService ssExamArticleRecordService,
                           SsExamArticleQuestionRecordService ssExamArticleQuestionRecordService,
                           SsTaskTaskRecordService ssTaskTaskRecordService) {
        this.ssExamInfoService = ssExamInfoService;
        this.ssSysBasService = ssSysBasService;
        this.ssExamTestScheduleService = ssExamTestScheduleService;
        this.ssExamTestRecordService = ssExamTestRecordService;
        this.ssExamQuestionService = ssExamQuestionService;
        this.moneyService = moneyService;
        this.ssExamTestQuestionService = ssExamTestQuestionService;
        this.ssExamQuestionOptionService = ssExamQuestionOptionService;
        this.ssExamPaperService = ssExamPaperService;
        this.ssExamPaperRecordService = ssExamPaperRecordService;
        this.ssExamPaperQuestionService = ssExamPaperQuestionService;
        this.ssExamPaperQuestionRecordService = ssExamPaperQuestionRecordService;
        this.ssExamErrorQuestionService = ssExamErrorQuestionService;
        this.ssExamErrorRecordService = ssExamErrorRecordService;
        this.ssExamCollectionQuestionService = ssExamCollectionQuestionService;
        this.ssExamPaperTypeHotService = ssExamPaperTypeHotService;
        this.ssArticleArticleService = ssArticleArticleService;
        this.ssExamArticleRecordService = ssExamArticleRecordService;
        this.ssExamArticleQuestionRecordService = ssExamArticleQuestionRecordService;
        this.ssTaskTaskRecordService = ssTaskTaskRecordService;
    }

    @Override
    public ExamInfoVO getInfosMy(String userId) {
        SsExamInfo ssExamInfo = ssExamInfoService.getByUserId(userId);
        if (ssExamInfo == null) {
            return new ExamInfoVO("");
        }
        ExamInfoVO examInfoVO = new ExamInfoVO(ssExamInfo);
        setExamInfoVO(examInfoVO);
        return examInfoVO;
    }


    @Override
    public void putInfosMy(String userId, ExamInfoVO examInfoVO) {
        validExamInfoVO(examInfoVO);
        SsExamInfo ssExamInfo = ssExamInfoService.getByUserId(userId);
        if (ssExamInfo == null) {
            ssExamInfo = new SsExamInfo(userId);
            setExamInfo(ssExamInfo, examInfoVO);
            ssExamInfoService.save(ssExamInfo);
        }
        setExamInfo(ssExamInfo, examInfoVO);
        ssExamInfoService.update(ssExamInfo);
    }

    @Override
    public TestsOutVo getTestsMy(String userId) {
        TestsOutVo testsOutVo;
        //获取刷题进度信息
        SsExamTestSchedule ssExamTestSchedule = ssExamTestScheduleService.getNotHistoryByUserId(userId);
        //如果为空。。
        if (ssExamTestSchedule == null) {
            testsOutVo = new TestsOutVo();
            testsOutVo.setStatus(TestsOutVo.Status.NOT_PLAN);
            return testsOutVo;
        }
        testsOutVo = new TestsOutVo(ssExamTestSchedule);
        //如果不为空，刷新总题数，总阶段数
        updateQuestionNumberAndStageNumber(ssExamTestSchedule, testsOutVo);
        //判断状态
        String status = getTestsStatus(ssExamTestSchedule);
        //如果NowStage > 1 ,判断是否有刷题权限（是否vip）
        if (ssExamTestSchedule.getNowStage() > 1) {
            boolean isVip = moneyService.vipMy(userId).isVip();
            if (!isVip) {
                testsOutVo.setAuthorization(false);
            }
        }
        //返回数据
        testsOutVo.setQuestionNumber(ssExamTestSchedule.getQuestionNumber());
        testsOutVo.setStageNumber(ssExamTestSchedule.getStageNumber());
        setExamInfoVO(testsOutVo);
        testsOutVo.setStatus(status);
        setNowQuestion(testsOutVo, ssExamTestSchedule);//设置当前完成题数
        return testsOutVo;
    }

    @Override
    public void postTests(String userId) {
        //如果已有计划，修改为过期
        SsExamTestSchedule ssExamTestSchedule = ssExamTestScheduleService
                .getNotHistoryByUserId(userId);
        if (ssExamTestSchedule != null) {
            ssExamTestSchedule.setScheduleHistoryNumber(ssExamTestSchedule.getId());
            ssExamTestScheduleService.update(ssExamTestSchedule);
        }
        //获取当前考试信息，计算考试题数，阶段数,新增计划
        SsExamInfo ssExamInfo = ssExamInfoService.getByUserId(userId);
        if (ssExamInfo == null
                || StringUtils.isBlank(ssExamInfo.getExamType())
                || StringUtils.isBlank(ssExamInfo.getExamProvince())
                || StringUtils.isBlank(ssExamInfo.getExamDate())
                || StringUtils.isBlank(ssExamInfo.getExamRange())) {
            throw new ServiceException("请先完善考试信息");
        }
        int questionNumber = getQuestionNumber(new ExamInfoVO(ssExamInfo));
        int stageNumber = getStageNumber(questionNumber);
        ssExamTestSchedule = new SsExamTestSchedule();
        ssExamTestSchedule.setUserId(userId);
        ssExamTestSchedule.setExamType(ssExamInfo.getExamType());
        ssExamTestSchedule.setExamProvince(ssExamInfo.getExamProvince());
        ssExamTestSchedule.setExamCity(ssExamInfo.getExamCity());
        ssExamTestSchedule.setExamDate(ssExamInfo.getExamDate());
        ssExamTestSchedule.setExamRange(ssExamInfo.getExamRange());
        ssExamTestSchedule.setQuestionNumber(questionNumber);
        ssExamTestSchedule.setStageNumber(stageNumber);
        ssExamTestSchedule.setNowStage(1);
        ssExamTestSchedule.setLastSynTime(DateUtils.formatDate(new Date(System.currentTimeMillis()), "yyyyMMddHHmmss"));
        ssExamTestScheduleService.saveSelective(ssExamTestSchedule);
    }

    @Override
    public TestRecordVo postTestRecords(String userId) {
        // 获取当前刷题计划
        SsExamTestSchedule schedule = ssExamTestScheduleService.getNotHistoryByUserId(userId);
        if (schedule == null) {
            throw new ServiceException("请制定全真刷题计划");
        }
        //如果NowStage > 1 判断是否vip
        if (schedule.getNowStage() > 1) {
            boolean isVip = moneyService.vipMy(userId).isVip();
            if (!isVip) {
                throw new ServiceException(Result.Status.UPGRADE_VIP, "请升级VIP");
            }
        }
        //获取当前刷题记录是否完成
        SsExamTestRecord record = ssExamTestRecordService.getByStageAndScheduleId(schedule.getNowStage(),
                schedule.getId());
        if (record != null) {
            //如果未完成，取练习记录，返回
            if (ExamConsts.TestState.PROCESSING.equals(record.getTestState())) {
                List<TestRecordVo.ExamQuestion> questions = new ArrayList<>();
                List<SsExamTestQuestion> testQuestions = ssExamTestQuestionService.queryByRecordId(record.getId());
                for (SsExamTestQuestion testQuestion : testQuestions) {
                    SsExamQuestion ssExamQuestion = ssExamQuestionService.getById(testQuestion.getQuestionId());
                    TestRecordVo.ExamQuestion examQuestion = getExamQuestion(ssExamQuestion, testQuestion, userId);
                    questions.add(examQuestion);
                }
                return new TestRecordVo(record, questions);
            } else if (ExamConsts.TestState.FINISH.equals(record.getTestState())
                    && schedule.getNowStage().compareTo(schedule.getStageNumber()) >= 0) {
                //已完成全部题目
                throw new ServiceException("已全部作答完成");
            }
        }
        //如果完成，取新题，记录进度和练习记录
        List<SsExamQuestion> ssExamQuestions = queryNumByTestScheduleWithNotTest(schedule);
        if (ssExamQuestions.isEmpty()) {
            throw new ServiceException("题目已全部作答");
        }
        record = new SsExamTestRecord();
        record.setUserId(userId);
        record.setStageNumber(schedule.getNowStage());
        record.setTestState(ExamConsts.TestState.PROCESSING);
        record.setQuestionNumber(ssExamQuestions.size());
        record.setTestScheduleId(schedule.getId());
        record.setFinishNumber(0);
        record.setUserTime(0);
        record.setCorrectNumber(0);
        record.setCorrectAvgRate(BigDecimal.valueOf(0.00));
        record.setBeatRate(BigDecimal.valueOf(0.00));
        ssExamTestRecordService.insertSelective(record);
        BigDecimal coefficient = new BigDecimal(0);//难度系数
        List<TestRecordVo.ExamQuestion> questions = new ArrayList<>();
        for (int i = 0; i < ssExamQuestions.size(); i++) {
            SsExamQuestion ssExamQuestion = ssExamQuestions.get(i);

            coefficient = coefficient.add(ssExamQuestion.getCoefficient());

            //练习记录题目
            SsExamTestQuestion testQuestion = new SsExamTestQuestion();
            testQuestion.setRecordId(record.getId());
            testQuestion.setQuestionId(ssExamQuestion.getId());
            testQuestion.setSequence(i + 1);
            testQuestion.setTestScheduleId(schedule.getId());
            testQuestion.setUserAnswer("[]");
            testQuestion.setIsTrue((byte) 0);
            ssExamTestQuestionService.insertSelective(testQuestion);

            TestRecordVo.ExamQuestion examQuestion = getExamQuestion(ssExamQuestion, testQuestion, userId);
            questions.add(examQuestion);
        }

        coefficient = coefficient.divide(new BigDecimal(record.getQuestionNumber()), 2, BigDecimal.ROUND_DOWN);
        record.setCoefficient(coefficient);
        ssExamTestRecordService.updateByPrimaryKeySelective(record);

        return new TestRecordVo(record, questions);
    }

    @Override
    public void putTestRecordsById(Long recordId, TestRecordsByIdInVo testRecordsByIdInVo) {
        SsExamTestRecord ssExamTestRecord = ssExamTestRecordService.getById(recordId);
        synTestRecords(ssExamTestRecord, testRecordsByIdInVo);
        ssExamTestRecordService.updateByPrimaryKeySelective(ssExamTestRecord);
    }

    @Override
    public void postTestRecordsById(Long recordId, TestRecordsByIdInVo testRecordsByIdInVo) {
        Integer correctNumber = 0;
        BigDecimal correctAvgRate = BigDecimal.valueOf(0.00).setScale(2, BigDecimal.ROUND_DOWN);
        BigDecimal beatRate;

        long totalQuestionNumber = 0L;
        long totalCorrectNumber = 0L;

        SsExamTestRecord ssExamTestRecord = ssExamTestRecordService.getById(recordId);
        synTestRecords(ssExamTestRecord, testRecordsByIdInVo);//同步作答记录
        List<SsExamTestQuestion> ssExamTestQuestions = ssExamTestQuestionService.queryByRecordId(recordId);
        for (SsExamTestQuestion ssExamTestQuestion : ssExamTestQuestions) {
            //修改试卷记录题目作答正确性
            boolean isTrue = isTrue(ssExamTestQuestion.getQuestionId(), ssExamTestQuestion.getUserAnswer());
            ssExamTestQuestion.setIsTrue((byte) (isTrue ? 1 : 0));
            ssExamTestQuestionService.updateByPrimaryKeySelective(ssExamTestQuestion);
            //计算正确数
            if (isTrue) {
                correctNumber++;
            }
            //更新选择题作答人数和正确人数
            SsExamQuestion ssExamQuestion = ssExamQuestionService.getById(ssExamTestQuestion.getQuestionId());
            updateTestNumberAndaddErrorQuestion(ssExamQuestion, ssExamTestRecord.getUserId(),
                    ssExamTestQuestion.getUserAnswer(), isTrue);

            totalQuestionNumber += ssExamQuestion.getTestNumber();
            totalCorrectNumber += ssExamQuestion.getCorrectNumber();
        }
        //计算平均正确率
        correctAvgRate = computeCorrectAvgRate(totalCorrectNumber, totalQuestionNumber);

        //计算击败率
        int totalNumber = ssExamTestRecordService.countSubmittedAll();
        int lessCorrectNumber = ssExamTestRecordService.countSubmittedByLessOrEqualsCorrectNumber(correctNumber);
        beatRate = computeBeatRate(lessCorrectNumber, totalNumber);


        //更新作答记录状态
        ssExamTestRecord.setTestState(ExamConsts.TestState.FINISH);
        ssExamTestRecord.setCorrectNumber(correctNumber);
        ssExamTestRecord.setCorrectAvgRate(correctAvgRate);
        ssExamTestRecord.setBeatRate(beatRate);
        ssExamTestRecordService.updateByPrimaryKeySelective(ssExamTestRecord);

        //更新考试进度，阶段和题数信息
        SsExamTestSchedule schedule = ssExamTestScheduleService.getById(ssExamTestRecord.getTestScheduleId());
        schedule.setNowQuestion(schedule.getNowQuestion() + ssExamTestRecord.getQuestionNumber());
        if (schedule.getNowStage().compareTo(schedule.getStageNumber()) <= 0) {
            schedule.setNowStage(schedule.getNowStage() + 1);
        }
        ssExamTestScheduleService.update(schedule);

        //处理全真刷题任务
        ssTaskTaskRecordService.finishTask(schedule.getUserId(), 3L);
    }

    @Override
    public TestRecordVo getTestRecordsById(Long recordId) {
        SsExamTestRecord ssExamTestRecord = ssExamTestRecordService.getById(recordId);
        if (ssExamTestRecord == null) {
            throw new ServiceException("作答报告不存在");
        }
        List<TestRecordVo.ExamQuestion> questions = new ArrayList<>();
        List<SsExamTestQuestion> testQuestions = ssExamTestQuestionService.queryByRecordId(ssExamTestRecord.getId());
        for (SsExamTestQuestion testQuestion : testQuestions) {
            SsExamQuestion ssExamQuestion = ssExamQuestionService.getById(testQuestion.getQuestionId());
            TestRecordVo.ExamQuestion examQuestion = getExamQuestion(ssExamQuestion, testQuestion, ssExamTestRecord.getUserId());
            questions.add(examQuestion);
        }
        TestRecordVo testRecordVo = new TestRecordVo(ssExamTestRecord, questions);
        testRecordVo.setCorrectRate(
                computeCorrectRate(testRecordVo.getCorrectNumber(), testRecordVo.getQuestionNumber()));
        return testRecordVo;
    }

    @Override
    public List<TestRecordsListOutVO> getTestRecordsListMy(TestRecordsListInVO testRecordsListInVO) {
        SsExamTestSchedule schedule = ssExamTestScheduleService.getNotHistoryByUserId(testRecordsListInVO.getUserId());
        testRecordsListInVO.setTestScheduleId(schedule.getId());
        List<SsExamTestRecord> ssExamTestRecords = ssExamTestRecordService.queryByTestScheduleId(testRecordsListInVO);
        List<TestRecordsListOutVO> listOutVOS = new ArrayList<>();
        for (SsExamTestRecord ssExamTestRecord : ssExamTestRecords) {
            TestRecordsListOutVO testRecordsListOutVO = new TestRecordsListOutVO(ssExamTestRecord);
            listOutVOS.add(testRecordsListOutVO);
        }
        return listOutVOS;
    }

    @Override
    public void deleteTests(String userId) {
        SsExamTestSchedule ssExamTestSchedule = ssExamTestScheduleService.getNotHistoryByUserId(userId);
        if (ssExamTestSchedule != null) {
            ssExamTestSchedule.setScheduleHistoryNumber(ssExamTestSchedule.getId());
            ssExamTestScheduleService.update(ssExamTestSchedule);
        }
    }

    @Override
    public List<PaperListOutVO> getPapers(String userId, PaperListInVO paperListInVO) {
        if (StringUtils.isNotBlank(paperListInVO.getPaperType())) {
            addPaperTypeHot(paperListInVO.getPaperType(), 1L);//统计分类热度
        }
        List<SsExamPaper> papers = ssExamPaperService.query(paperListInVO);
        List<PaperListOutVO> list = new ArrayList<>();
        for (SsExamPaper ssExamPaper : papers) {
            PaperListOutVO paperListOutVO = new PaperListOutVO(ssExamPaper);
            if (StringUtils.isNotBlank(userId)) {
                SsExamPaperRecord record = ssExamPaperRecordService
                        .getNotHistoryByUserIdAndPaperId(userId, ssExamPaper.getId());
                if (record != null) {
                    paperListOutVO.setPaperRecordId(record.getId());
                    paperListOutVO.setTestState(record.getTestState());
                    paperListOutVO.setFinishNumber(record.getFinishNumber());
                    if (record.getTestState().equals(ExamConsts.TestState.FINISH)) {
                        BigDecimal correctRate = BigDecimal.valueOf(record.getCorrectNumber())
                                .divide(BigDecimal.valueOf(record.getQuestionNumber()),
                                        2, BigDecimal.ROUND_DOWN);
                        paperListOutVO.setCorrectRate(correctRate);
                        paperListOutVO.setFinishNumber(record.getQuestionNumber());
                    }
                }
            }
            //考试类型转汉字
            if (StringUtils.isNotBlank(paperListOutVO.getPaperType())) {
                SsSysBas sysBas = ssSysBasService.getByBasTypeAndDictValue(SysBasConsts.EXAM_TYPE, paperListOutVO.getPaperType());
                if (sysBas.getDictValue().equals("00")) {
                    sysBas.setDictName("各类考试通用");
                }
                paperListOutVO.setPaperType(sysBas.getDictName());
            }

            list.add(paperListOutVO);
        }
        return list;
    }


    @Override
    public PaperRecordOutVO postPaperRecords(String userId, Long paperId) {
        SsExamPaper ssExamPaper = ssExamPaperService.getById(paperId);
        if (ssExamPaper == null) {
            throw new ServiceException("试卷不存在");
        }
        addPaperTypeHot(ssExamPaper.getPaperType(), 2L);//统计分类热度
        boolean isVip = moneyService.vipMy(userId).isVip();//获取vip状态
        int freeQuestionNumber;//免费题数
        if (isVip) {
            freeQuestionNumber = ssExamPaper.getQuestionNumber();
        } else {
            freeQuestionNumber = getFreeQuestionNumber(ssExamPaper.getQuestionNumber());
        }
        //获取当前刷题记录,当刷题记录为空或者已经完成，新增记录
        SsExamPaperRecord ssExamPaperRecord = ssExamPaperRecordService.getNotHistoryByUserIdAndPaperId(userId, paperId);
        boolean finish = false;
        if (ssExamPaperRecord != null) {
            finish = ExamConsts.TestState.FINISH.equals(ssExamPaperRecord.getTestState());
            if (finish) {
                ssExamPaperRecord.setHistoryId(ssExamPaperRecord.getId());
                ssExamPaperRecordService.updateByPrimaryKey(ssExamPaperRecord);
            } else {
                //处理试卷题目数和记录表题目数不一致问题
                boolean eqQuestionNumber = ssExamPaperRecord.getQuestionNumber().equals(ssExamPaper.getQuestionNumber());
                if (!eqQuestionNumber) {
                    ssExamPaperRecord.setQuestionNumber(ssExamPaper.getQuestionNumber());
                    if (ssExamPaperRecord.getFinishNumber().compareTo(ssExamPaperRecord.getQuestionNumber()) > 0) {
                        ssExamPaperRecord.setFinishNumber(ssExamPaperRecord.getQuestionNumber());
                    }
                    ssExamPaperRecordService.updateByPrimaryKey(ssExamPaperRecord);
                }
            }
        }
        if (ssExamPaperRecord == null || finish) {
            ssExamPaperRecord = new SsExamPaperRecord();
            setSsExamPaperRecord(userId, ssExamPaper, ssExamPaperRecord);
            ssExamPaperRecordService.insert(ssExamPaperRecord);
        }

        //拼装数据返回
        List<PaperRecordOutVO.PaperQuestion> questions =
                getPaperQuestions(ssExamPaper.getId(), ssExamPaperRecord, freeQuestionNumber);
        PaperRecordOutVO paperRecordOutVO = new PaperRecordOutVO(ssExamPaperRecord);
        paperRecordOutVO.setQuestions(questions);
        return paperRecordOutVO;
    }

    @Override
    public void putRecords(Long recordId, PaperRecordInVO paperRecordInVO) {
        paperRecordInVO.setPaperRecordId(recordId);
        SsExamPaperRecord paperRecord = ssExamPaperRecordService.getById(recordId);
        synPaperRecords(paperRecord, paperRecordInVO);
        ssExamPaperRecordService.updateByPrimaryKey(paperRecord);
    }

    @Override
    public void postRecords(Long recordId, PaperRecordInVO paperRecordInVO) {
        paperRecordInVO.setPaperRecordId(recordId);
        SsExamPaperRecord paperRecord = ssExamPaperRecordService.getById(recordId);
        if (paperRecord != null && ExamConsts.TestState.FINISH.equals(paperRecord.getTestState())) {
            return;
        }
        synPaperRecords(paperRecord, paperRecordInVO);//同步作答记录

        Integer correctNumber = 0;
        BigDecimal correctAvgRate = BigDecimal.valueOf(0.00).setScale(2, BigDecimal.ROUND_DOWN);
        BigDecimal beatRate;

        long totalQuestionNumber = 0L;
        long totalCorrectNumber = 0L;

        List<SsExamPaperQuestionRecord> ssExamPaperQuestionRecords = ssExamPaperQuestionRecordService.findSequenceDescByRecordId(recordId);
        for (SsExamPaperQuestionRecord questionRecord : ssExamPaperQuestionRecords) {
            //修改练习记录题目作答正确性
            boolean isTrue = isTrue(questionRecord.getQuestionId(), questionRecord.getUserAnswer());
            questionRecord.setIsTrue((byte) (isTrue ? 1 : 0));
            ssExamPaperQuestionRecordService.updateByPrimaryKeySelective(questionRecord);

            //计算正确数
            if (isTrue) {
                correctNumber++;
            }

            SsExamQuestion ssExamQuestion = ssExamQuestionService.getById(questionRecord.getQuestionId());
            //更新选择题作答人数和正确人数
            updateTestNumberAndaddErrorQuestion(ssExamQuestion, paperRecord.getUserId(),
                    questionRecord.getUserAnswer(), isTrue);

            totalQuestionNumber += ssExamQuestion.getTestNumber();
            totalCorrectNumber += ssExamQuestion.getCorrectNumber();
        }


        //计算平均正确率
        correctAvgRate = computeCorrectAvgRate(totalCorrectNumber, totalQuestionNumber);

        //计算击败率
        int totalNumber = ssExamPaperRecordService.countSubmittedByPaperId(paperRecord.getPaperId());
        int lessCorrectNumber = ssExamPaperRecordService
                .countSubmittedByPaperIdWithLessOrEqualsCorrectNumber(paperRecord.getPaperId(), correctNumber);
        beatRate = computeBeatRate(lessCorrectNumber, totalNumber);

        //更新作答记录状态
        paperRecord.setTestState(ExamConsts.TestState.FINISH);
        paperRecord.setCorrectNumber(correctNumber);
        paperRecord.setCorrectAvgRate(correctAvgRate);
        paperRecord.setBeatRate(beatRate);
//        paperRecord.setFinishNumber(paperRecord.getQuestionNumber());
        ssExamPaperRecordService.updateByPrimaryKey(paperRecord);
    }

    @Override
    public PaperRecordOutVO getRecords(Long recordId) {
        SsExamPaperRecord ssExamPaperRecord = ssExamPaperRecordService.getById(recordId);
        if (ssExamPaperRecord == null) {
            throw new ServiceException("作答报告不存在");
        }
        List<PaperRecordOutVO.PaperQuestion> questions =
                getPaperQuestions(ssExamPaperRecord.getPaperId(), ssExamPaperRecord, ssExamPaperRecord.getQuestionNumber());
        PaperRecordOutVO paperRecordOutVO = new PaperRecordOutVO(ssExamPaperRecord);
        paperRecordOutVO.setQuestions(questions);
        paperRecordOutVO.setCorrectRate(
                computeCorrectRate(paperRecordOutVO.getCorrectNumber(), paperRecordOutVO.getQuestionNumber()));
        return paperRecordOutVO;
    }

    @Override
    public List<QuestionErrorListOutVO> getErrors(QuestionErrorCollectionListInVO questionErrorCollectionListInVO) {
        List<QuestionErrorListOutVO> questionErrorListOutVOS = ssExamErrorQuestionService
                .countGroupByBelongYearMonthByUserId(questionErrorCollectionListInVO);
        List<SsExamErrorRecord> records = ssExamErrorRecordService
                .findBelongYearMonthDescByUserId(questionErrorCollectionListInVO.getUserId());
        for (QuestionErrorListOutVO errorListVO : questionErrorListOutVOS) {
            errorListVO.setIndexNumber(0);
            for (int i = records.size() - 1; i >= 0; i--) {
                SsExamErrorRecord ssExamErrorRecord = records.get(i);
                if (ssExamErrorRecord.getBelongYearMonth().equals(errorListVO.getBelongYearMonth())) {
                    errorListVO.setIndexNumber(ssExamErrorRecord.getIndexNumber());
                    records.remove(i);
                }
            }
            String errorTitle = getMonthGroupQuestionTitle(errorListVO.getBelongYearMonth());
            errorListVO.setErrorTitle(errorTitle);
            try {
                errorListVO.setBelongYearMonth(URLEncoder.encode(errorListVO.getBelongYearMonth(), "utf-8"));
            } catch (UnsupportedEncodingException e) {
                throw new RuntimeException("url编码出错", e);
            }
        }
        return questionErrorListOutVOS;
    }

    @Override
    public QuestionErrorVO getErrorQuestions(String userId, String belongYearMonth) {
        int indexNumber = 0;
        SsExamErrorRecord ssExamErrorRecord = ssExamErrorRecordService.getByUserIdWithBelongYearMonth(userId, belongYearMonth);
        if (ssExamErrorRecord != null) {
            indexNumber = ssExamErrorRecord.getIndexNumber();
        }
        List<SsExamErrorQuestion> ssExamErrorQuestions = ssExamErrorQuestionService
                .findByUserIdWithBelongYearMonth(userId, belongYearMonth);
        List<QuestionErrorVO.ErrorQuestion> errorQuestions = new ArrayList<>();
        for (SsExamErrorQuestion ssExamErrorQuestion : ssExamErrorQuestions) {
            SsExamQuestion question = ssExamQuestionService.getById(ssExamErrorQuestion.getQuestionId());
            if (question == null) {
                throw new ServiceException(String.format("id : [%s] 的选择题不存在", ssExamErrorQuestion.getQuestionId()));
            }
            QuestionErrorVO.ErrorQuestion errorQuestion = new QuestionErrorVO.ErrorQuestion(ssExamErrorQuestion, question);
            errorQuestion.setOptions(getOptions(question.getId()));
            errorQuestion.setIsCollection(getIsCollection(userId, question.getId()));
            errorQuestions.add(errorQuestion);
        }

        QuestionErrorVO questionErrorVO = new QuestionErrorVO();
        questionErrorVO.setErrorTitle(String.format("%s · 错题", getMonthGroupQuestionTitle(belongYearMonth)));
        questionErrorVO.setIndexNumber(indexNumber);
        questionErrorVO.setBelongYearMonth(belongYearMonth);
        questionErrorVO.setQuestionNumber(ssExamErrorQuestions.size());
        questionErrorVO.setQuestions(errorQuestions);
        return questionErrorVO;
    }

    @Override
    public void putErrorRecords(String belongYearMonth, ErrorRecordsInVO errorRecordsInVO) {
        SsExamErrorRecord record = ssExamErrorRecordService
                .getByUserIdWithBelongYearMonth(errorRecordsInVO.getUserId(), belongYearMonth);
        int indexNumber = ssExamErrorQuestionService.countLessTanErrorQuestionId(belongYearMonth, errorRecordsInVO);
        if (record == null) {
            record = new SsExamErrorRecord();
            record.setBelongYearMonth(belongYearMonth);
            record.setUserId(errorRecordsInVO.getUserId());
            record.setIndexNumber(indexNumber);
            record.setErrorQuestionId(errorRecordsInVO.getErrorQuestionId());
            ssExamErrorRecordService.insertSelective(record);
        }
        record.setIndexNumber(indexNumber);
        record.setErrorQuestionId(errorRecordsInVO.getErrorQuestionId());
        ssExamErrorRecordService.updateByPrimaryKey(record);

    }

    @Override
    public void deleteErrorQuestion(Long errorQuestionId) {
        SsExamErrorQuestion ssExamErrorQuestion = ssExamErrorQuestionService.selectByPrimaryKey(errorQuestionId);
        if (ssExamErrorQuestion == null) {
            return;
        }
        int number = ssExamErrorQuestionService.countUserIdWithBelongYearMonth(
                ssExamErrorQuestion.getUserId(), ssExamErrorQuestion.getBelongYearMonth());
        if (number == 1) {
            ssExamErrorRecordService.deleteByUserIdWithBelongYearMonth(
                    ssExamErrorQuestion.getUserId(), ssExamErrorQuestion.getBelongYearMonth());
        }
        ssExamErrorQuestionService.deleteByPrimaryKey(errorQuestionId);
    }

    @Override
    public void deleteErrorQuestions(String userId) {
        //清空错题
        ssExamErrorQuestionService.deleteByUserId(userId);
        //清空错题作答记录
        ssExamErrorRecordService.deleteByUserId(userId);
    }

    @Override
    public List<SsSysBas> getPaperTypes() {
        List<SsSysBas> list = ssSysBasService.queryNotTypeByBasType(SysBasConsts.EXAM_TYPE);
        SsSysBas ssSysBas = new SsSysBas();
        ssSysBas.setDictName("各类考试通用");
        ssSysBas.setDictValue("00");
        ssSysBas.setId(0L);
        ssSysBas.setBasType(SysBasConsts.EXAM_TYPE);
        ssSysBas.setSequence(0);
        ssSysBas.setIsFixed(0);
        ssSysBas.setAttr1("");
        ssSysBas.setAttr2("");
        ssSysBas.setAttr3("");
        list.add(0, ssSysBas);
        return list;
    }

    @Override
    public List<SsSysBas> getPaperAreas() {
        List<SsSysBas> list = ssSysBasService.queryNotTypeByBasType(SysBasConsts.EXAM_AREA);
        SsSysBas ssSysBas = new SsSysBas();
        ssSysBas.setDictName("全国");
        ssSysBas.setDictValue("00");
        ssSysBas.setId(0L);
        ssSysBas.setBasType(SysBasConsts.EXAM_AREA);
        ssSysBas.setSequence(0);
        ssSysBas.setIsFixed(0);
        ssSysBas.setAttr1("");
        ssSysBas.setAttr2("");
        ssSysBas.setAttr3("");
        list.add(0, ssSysBas);
        return list;
    }

    @Override
    public List<SsSysBas> getPaperYearMonths() {
        List<String> belongYearMonths = ssExamPaperService.findByDistinctBelongYearMonth();
        List<SsSysBas> list = new ArrayList<>();
        for (String belongYearMonth : belongYearMonths) {
            SsSysBas ssSysBas = new SsSysBas();
            ssSysBas.setDictName(getFormatYearMonth(belongYearMonth));
            ssSysBas.setDictValue(belongYearMonth);
            ssSysBas.setId(0L);
            ssSysBas.setBasType("BelongYearMonth");
            ssSysBas.setSequence(0);
            ssSysBas.setIsFixed(0);
            ssSysBas.setAttr1("");
            ssSysBas.setAttr2("");
            ssSysBas.setAttr3("");
            list.add(ssSysBas);
        }
        return list;
    }

    @Override
    public List<QuestionCollectionListOutVO> getCollections(QuestionErrorCollectionListInVO questionErrorCollectionListInVO) {
        List<QuestionCollectionListOutVO> questionCollectionListOutVOS = ssExamCollectionQuestionService
                .countGroupByBelongYearMonthByUserId(questionErrorCollectionListInVO);
        for (QuestionCollectionListOutVO listOutVO : questionCollectionListOutVOS) {
            String collectionTitle = getMonthGroupQuestionTitle(listOutVO.getBelongYearMonth());
            listOutVO.setCollectionTitle(collectionTitle);
            try {
                listOutVO.setBelongYearMonth(URLEncoder.encode(listOutVO.getBelongYearMonth(), "utf-8"));
            } catch (UnsupportedEncodingException e) {
                throw new RuntimeException("url编码出错", e);
            }
        }
        return questionCollectionListOutVOS;
    }

    @Override
    public QuestionCollectionVO getCollectionQuestions(String userId, String belongYearMonth) {
        List<SsExamCollectionQuestion> ssExamCollectionQuestions = ssExamCollectionQuestionService
                .findByUserIdWithBelongYearMonth(userId, belongYearMonth);
        List<QuestionCollectionVO.CollectionQuestion> collectionQuestions = new ArrayList<>();
        for (SsExamCollectionQuestion ssExamErrorQuestion : ssExamCollectionQuestions) {
            SsExamQuestion question = ssExamQuestionService.getById(ssExamErrorQuestion.getQuestionId());
            if (question == null) {
                throw new ServiceException(String.format("id : [%s] 的选择题不存在", ssExamErrorQuestion.getQuestionId()));
            }
            QuestionCollectionVO.CollectionQuestion collectionQuestion = new QuestionCollectionVO.CollectionQuestion(ssExamErrorQuestion, question);
            collectionQuestion.setOptions(getOptions(question.getId()));
            collectionQuestion.setIsCollection(true);
            collectionQuestions.add(collectionQuestion);
        }

        QuestionCollectionVO questionCollectionVO = new QuestionCollectionVO();
        questionCollectionVO.setCollectionTitle(String.format("%s · 收藏", getMonthGroupQuestionTitle(belongYearMonth)));
        questionCollectionVO.setBelongYearMonth(belongYearMonth);
        questionCollectionVO.setQuestionNumber(ssExamCollectionQuestions.size());
        questionCollectionVO.setQuestions(collectionQuestions);
        return questionCollectionVO;
    }

    @Override
    public void deleteCollectionQuestion(String userId, Long questionId) {
        ssExamCollectionQuestionService.deleteByUserIdWithQuestionId(userId, questionId);
    }

    @Override
    public void deleteCollectionQuestions(String userId) {
        ssExamCollectionQuestionService.deleteByUserId(userId);
    }

    @Override
    public void postCollectionQuestions(String userId, CollectionQuestionVO collectionQuestionVO) {
        SsExamQuestion ssExamQuestion = ssExamQuestionService.getById(collectionQuestionVO.getQuestionId());
        if (ssExamQuestion == null) {
            throw new ServiceException("选择题不存在");
        }
        SsExamCollectionQuestion ssExamCollectionQuestion = ssExamCollectionQuestionService
                .getByUserIdWithQuestionId(userId, ssExamQuestion.getId());
        if (ssExamCollectionQuestion != null) {
            return;
        }

        String belongYearMonth = formatBelongYearMonth(ssExamQuestion.getBelongDate());
        ssExamCollectionQuestion = new SsExamCollectionQuestion();
        ssExamCollectionQuestion.setUserId(userId);
        ssExamCollectionQuestion.setQuestionId(collectionQuestionVO.getQuestionId());
        ssExamCollectionQuestion.setBelongYearMonth(belongYearMonth);
        ssExamCollectionQuestionService.insertSelective(ssExamCollectionQuestion);
    }

    @Override
    public List<PaperTypeHotVO> getPaperTypeHots() {
        List<SsExamPaperTypeHot> ssExamPaperTypeHots = ssExamPaperTypeHotService.findOrderHotNumberDesc();
        List<PaperTypeHotVO> paperTypeHotVOS = new ArrayList<>();
        for (SsExamPaperTypeHot ssExamPaperTypeHot : ssExamPaperTypeHots) {
            PaperTypeHotVO paperTypeHotVO = new PaperTypeHotVO();
            paperTypeHotVO.setTypeName(ssExamPaperTypeHot.getTypeName());
            paperTypeHotVO.setTypeValue(ssExamPaperTypeHot.getTypeValue());
            paperTypeHotVOS.add(paperTypeHotVO);
        }
        return paperTypeHotVOS;
    }

    @Override
    public ArticleTestRecordOutVO postArticleRecords(String userId, Long articleId) {
        //取文章判断类型取对应的题库范围
        SsArticleArticle ssArticle = ssArticleArticleService.getById(articleId);
        if (ssArticle == null) {
            throw new ServiceException(String.format("文章,id :[%s}不存在", articleId));
        }
        //判断是否有测评
        if (!isEvaluation(ssArticle)) {
            throw new ServiceException("该文章没有测评");
        }
        //判断是否有权限（日报当天免费，其他收费）
        if (!isAuthorization(ssArticle, userId)) {
            throw new ServiceException(Result.Status.UPGRADE_VIP, "请升级VIP");
        }
        //判断是否有非历史记录未交卷
        SsExamArticleRecord ssExamArticleRecord = ssExamArticleRecordService
                .getNotHistoryByUserIdAndArticleId(userId, articleId);
        List<SsExamArticleQuestionRecord> questionRecords = new ArrayList<>();
        List<SsExamQuestion> ssExamQuestions = new ArrayList<>();
        if (ssExamArticleRecord != null) {
            if (ExamConsts.TestState.PROCESSING.equals(ssExamArticleRecord.getTestState())) {
                questionRecords = ssExamArticleQuestionRecordService.findByArticleRecordId(ssExamArticleRecord.getId());
                for (SsExamArticleQuestionRecord questionRecord : questionRecords) {
                    SsExamQuestion ssExamQuestion = ssExamQuestionService.getById(questionRecord.getQuestionId());
                    ssExamQuestions.add(ssExamQuestion);
                }
                return getArticleTestRecordOutVO(ssExamArticleRecord, questionRecords, ssExamQuestions);
            } else {
                ssExamArticleRecord.setHistoryId(ssExamArticleRecord.getId());
                ssExamArticleRecordService.updateByPrimaryKeySelective(ssExamArticleRecord);
            }
        }
        //根据条件取题库
        String belongDate = ssArticle.getArticleTypeId().equals(3L)
                ? ssArticle.getBelongDate() : ssArticle.getBelongDate().substring(0, 7);
        ssExamQuestions = ssExamQuestionService.queryByLikeBelongDate(belongDate);
        if (ssExamQuestions.isEmpty()) {
            throw new ServiceException("该文章当前没有测评题目");
        }
        //生成记录和题目记录
        ssExamArticleRecord = new SsExamArticleRecord();
        setSsExamArticleRecord(userId, ssArticle, ssExamArticleRecord);
        ssExamArticleRecordService.insertSelective(ssExamArticleRecord);
        int sequence = 0;
        BigDecimal coefficient = BigDecimal.valueOf(0.0);
        for (SsExamQuestion ssExamQuestion : ssExamQuestions) {
            SsExamArticleQuestionRecord questionRecord = new SsExamArticleQuestionRecord();
            questionRecord.setArticleRecordId(ssExamArticleRecord.getId());
            questionRecord.setQuestionId(ssExamQuestion.getId());
            questionRecord.setSequence(++sequence);
            questionRecord.setUserAnswer("[]");
            questionRecord.setIsTrue((byte) 0);
            questionRecords.add(questionRecord);
            //难度系数
            coefficient = coefficient.add(ssExamQuestion.getCoefficient());
        }
        ssExamArticleQuestionRecordService.batchInsert(questionRecords);

        coefficient = coefficient.divide(BigDecimal.valueOf(sequence), 2, BigDecimal.ROUND_DOWN);
        ssExamArticleRecord.setQuestionNumber(sequence);
        ssExamArticleRecord.setCoefficient(coefficient);
        ssExamArticleRecordService.updateByPrimaryKeySelective(ssExamArticleRecord);

        //返回数据
        return getArticleTestRecordOutVO(ssExamArticleRecord, questionRecords, ssExamQuestions);
    }

    @Override
    public void putArticleRecords(Long recordId, ArticleRecordInVO articleRecordInVO) {
        SsExamArticleRecord record = ssExamArticleRecordService.selectByPrimaryKey(recordId);
        synArticleRecords(articleRecordInVO, record);
    }

    @Override
    public void postArticleRecordsById(Long recordId, ArticleRecordInVO articleRecordInVO) {
        Integer correctNumber = 0;
        BigDecimal correctAvgRate = BigDecimal.valueOf(0.00).setScale(2, BigDecimal.ROUND_DOWN);
        BigDecimal beatRate;

        long totalQuestionNumber = 0L;
        long totalCorrectNumber = 0L;

        SsExamArticleRecord record = ssExamArticleRecordService.selectByPrimaryKey(recordId);
        //同步进度
        synArticleRecords(articleRecordInVO, record);
        List<SsExamArticleQuestionRecord> questionRecords = ssExamArticleQuestionRecordService.findByArticleRecordId(recordId);
        for (SsExamArticleQuestionRecord questionRecord : questionRecords) {
            //修改题目作答正确性
            boolean isTrue = isTrue(questionRecord.getQuestionId(), questionRecord.getUserAnswer());
            questionRecord.setIsTrue((byte) (isTrue ? 1 : 0));
            ssExamArticleQuestionRecordService.updateByPrimaryKeySelective(questionRecord);

            //计算正确数
            if (isTrue) {
                correctNumber++;
            }

            SsExamQuestion ssExamQuestion = ssExamQuestionService.getById(questionRecord.getQuestionId());
            //更新选择题作答人数和正确人数
            updateTestNumberAndaddErrorQuestion(ssExamQuestion, record.getUserId(),
                    questionRecord.getUserAnswer(), isTrue);

            totalQuestionNumber += ssExamQuestion.getTestNumber();
            totalCorrectNumber += ssExamQuestion.getCorrectNumber();
        }

        //计算平均正确率
        correctAvgRate = computeCorrectAvgRate(totalCorrectNumber, totalQuestionNumber);

        //计算击败率
        int totalNumber = ssExamArticleRecordService.countSubmittedByArticleId(record.getArticleId());
        int lessCorrectNumber = ssExamArticleRecordService
                .countSubmittedByArticleIdWithLessOrEqualsCorrectNumber(record.getArticleId(), correctNumber);
        beatRate = computeBeatRate(lessCorrectNumber, totalNumber);

        //更新作答记录状态
        record.setTestState(ExamConsts.TestState.FINISH);
        record.setCorrectNumber(correctNumber);
        record.setCorrectAvgRate(correctAvgRate);
        record.setBeatRate(beatRate);
        record.setFinishNumber(record.getQuestionNumber());
        ssExamArticleRecordService.updateByPrimaryKey(record);

        //处理文章刷题任务
        ssTaskTaskRecordService.finishTask(record.getUserId(), 2L);
    }

    /**
     * 计算击败率
     *
     * @param lessCorrectNumber
     * @param totalNumber
     * @return
     */
    private BigDecimal computeBeatRate(int lessCorrectNumber, int totalNumber) {
        BigDecimal beatRate = BigDecimal.valueOf(1.00);
        if (totalNumber > 0) {
            beatRate = BigDecimal.valueOf(lessCorrectNumber).divide(BigDecimal.valueOf(totalNumber), 2, BigDecimal.ROUND_DOWN);
        }
        return beatRate;
    }

    @Override
    public ArticleTestRecordOutVO getArticleRecords(Long recordId) {
        SsExamArticleRecord record = ssExamArticleRecordService.selectByPrimaryKey(recordId);
        if (record == null || !ExamConsts.TestState.FINISH.equals(record.getTestState())) {
            throw new ServiceException("测评报告不存在");
        }
        List<SsExamArticleQuestionRecord> questionRecords = ssExamArticleQuestionRecordService.findByArticleRecordId(record.getId());
        List<SsExamQuestion> ssExamQuestions = new ArrayList<>();
        for (SsExamArticleQuestionRecord questionRecord : questionRecords) {
            SsExamQuestion ssExamQuestion = ssExamQuestionService.getById(questionRecord.getQuestionId());
            ssExamQuestions.add(ssExamQuestion);
        }
        return getArticleTestRecordOutVO(record, questionRecords, ssExamQuestions);
    }

    /**
     * 计算平均正确率
     *
     * @param totalCorrectNumber
     * @param totalQuestionNumber
     * @return
     */
    private BigDecimal computeCorrectAvgRate(long totalCorrectNumber, long totalQuestionNumber) {
        BigDecimal correctAvgRate = BigDecimal.valueOf(0.0);
        if (totalQuestionNumber > 0) {
            correctAvgRate = BigDecimal.valueOf(totalCorrectNumber)
                    .divide(BigDecimal.valueOf(totalQuestionNumber), 2, BigDecimal.ROUND_DOWN);
        }
        return correctAvgRate;
    }

    /**
     * 更新选择题作答人数和正确人数
     *
     * @param ssExamQuestion
     * @param userId
     * @param userAnswer
     * @param isTrue
     */
    private void updateTestNumberAndaddErrorQuestion(SsExamQuestion ssExamQuestion, String userId, String userAnswer, boolean isTrue) {
        ssExamQuestion.setTestNumber(ssExamQuestion.getTestNumber() + 1);
        if (isTrue) {
            ssExamQuestion.setCorrectNumber(ssExamQuestion.getCorrectNumber() + 1);
        } else {
            //记录错题，无作答不记录
            if (userAnswer.length() > 2) {
                addErrorQuestion(userId, ssExamQuestion);
            }
        }
        ssExamQuestionService.update(ssExamQuestion);
    }

    /**
     * 同步作答进度
     *
     * @param articleRecordInVO
     * @param record
     */
    private void synArticleRecords(ArticleRecordInVO articleRecordInVO, SsExamArticleRecord record) {
        if (record == null) {
            throw new ServiceException("测评记录不存在");
        }

        if (ExamConsts.TestState.FINISH.equals(record.getTestState())) {
            throw new ServiceException("您已交卷");
        }
        record.setFinishNumber(articleRecordInVO.getFinishNumber());
        record.setUserTime(articleRecordInVO.getUserTime());
        ssExamArticleRecordService.updateByPrimaryKeySelective(record);

        List<ArticleRecordInVO.QuestionRecord> questions = articleRecordInVO.getQuestions();
        for (ArticleRecordInVO.QuestionRecord questionRecord : questions) {
            SsExamArticleQuestionRecord ssExamArticleQuestionRecord = ssExamArticleQuestionRecordService
                    .selectByPrimaryKey(questionRecord.getQuestionRecordId());
            List<Integer> userAnswers = questionRecord.getUserAnswers();
            ssExamArticleQuestionRecord.setUserAnswer(JSON.toJSONString(userAnswers));
            ssExamArticleQuestionRecordService.updateByPrimaryKeySelective(ssExamArticleQuestionRecord);
        }
    }

    /**
     * 拼凑 返回数据 体
     *
     * @param ssExamArticleRecord
     * @param questionRecords
     * @param ssExamQuestions
     * @return
     */
    private ArticleTestRecordOutVO getArticleTestRecordOutVO(SsExamArticleRecord ssExamArticleRecord,
                                                             List<SsExamArticleQuestionRecord> questionRecords,
                                                             List<SsExamQuestion> ssExamQuestions) {
        List<ArticleTestRecordOutVO.ArticleQuestion> articleQuestions =
                getArticleQuestion(questionRecords, ssExamQuestions, ssExamArticleRecord.getUserId());
        ArticleTestRecordOutVO vo = new ArticleTestRecordOutVO(ssExamArticleRecord);
        vo.setQuestions(articleQuestions);

        //计算正确率
        vo.setCorrectRate(computeCorrectRate(vo.getCorrectNumber(), vo.getQuestionNumber()));
        return vo;
    }

    /**
     * 计算正确率
     *
     * @param correctNumber
     * @param questionNumber
     * @return
     */
    private BigDecimal computeCorrectRate(Integer correctNumber, Integer questionNumber) {
        BigDecimal correctRate;
        if (correctNumber > 0) {
            correctRate = BigDecimal.valueOf(correctNumber)
                    .divide(BigDecimal.valueOf(questionNumber), 2, BigDecimal.ROUND_DOWN);
        } else {
            correctRate = BigDecimal.valueOf(0.0);
        }
        return correctRate;
    }

    /**
     * 拼凑选择题列表
     *
     * @param questionRecords
     * @param ssExamQuestions
     * @param userId
     * @return
     */
    private List<ArticleTestRecordOutVO.ArticleQuestion> getArticleQuestion(List<SsExamArticleQuestionRecord> questionRecords,
                                                                            List<SsExamQuestion> ssExamQuestions, String userId) {
        List<ArticleTestRecordOutVO.ArticleQuestion> list = new ArrayList<>();
        for (int i = 0; i < questionRecords.size(); i++) {
            SsExamArticleQuestionRecord questionRecord = questionRecords.get(i);
            SsExamQuestion ssExamQuestion = ssExamQuestions.get(i);
            List<Option> options = getOptions(ssExamQuestion.getId());
            boolean isEmphasis = ssExamQuestion.getIsEmphasis() == 1;
            boolean isCard = ssExamQuestion.getIsCard() == 1;
            boolean isTrue = questionRecord.getIsTrue() == 1;
            List<Integer> userAnswers = JSON.parseArray(questionRecord.getUserAnswer(), Integer.class);
            boolean isCollection = getIsCollection(userId, questionRecord.getQuestionId());
            ArticleTestRecordOutVO.ArticleQuestion articleQuestion = new ArticleTestRecordOutVO.ArticleQuestion();
            articleQuestion.setQuestionRecordId(questionRecord.getId());
            articleQuestion.setQuestionId(questionRecord.getQuestionId());
            articleQuestion.setSequence(questionRecord.getSequence());
            articleQuestion.setIsTrue(isTrue);
            articleQuestion.setQuestion(ssExamQuestion.getQuestion());
            articleQuestion.setAnalysis(ssExamQuestion.getAnalysis());
            articleQuestion.setQuestionType(ssExamQuestion.getQuestionType());
            articleQuestion.setIsEmphasis(isEmphasis);
            articleQuestion.setIsCard(isCard);
            articleQuestion.setOptions(options);
            articleQuestion.setUserAnswers(userAnswers);
            articleQuestion.setIsCollection(isCollection);
            articleQuestion.setOptions(getOptions(ssExamQuestion.getId()));
            list.add(articleQuestion);
        }

        return list;
    }

    private void setSsExamArticleRecord(String userId, SsArticleArticle article, SsExamArticleRecord ssExamArticleRecord) {
        ssExamArticleRecord.setUserId(userId);
        ssExamArticleRecord.setArticleId(article.getId());
        ssExamArticleRecord.setTitle(String.format("测评 · %s", article.getTitle()));
        ssExamArticleRecord.setTestState(ExamConsts.TestState.PROCESSING);
        ssExamArticleRecord.setQuestionNumber(0);
        ssExamArticleRecord.setFinishNumber(0);
        ssExamArticleRecord.setCorrectNumber(0);
        ssExamArticleRecord.setHistoryId(0L);
    }

    /**
     * 判断是否有权限（日报免费，其他收费）
     *
     * @param ssArticle
     * @param userId
     * @return
     */
    private boolean isAuthorization(SsArticleArticle ssArticle, String userId) {
        if (ssArticle.getArticleTypeId().equals(3L)) {
            return true;
        }
        boolean isVip = moneyService.vipMy(userId).isVip();
        if (isVip) {
            return true;
        }
        return false;
    }

    /**
     * 判断是否有测评
     *
     * @param ssArticle
     * @return
     */
    private boolean isEvaluation(SsArticleArticle ssArticle) {
        long articleTypeId = ssArticle.getArticleTypeId();
        return articleTypeId == 3L || articleTypeId == 4L;
    }


    /**
     * 统计试卷分类热度
     *
     * @param paperType
     * @param addHotNumber
     */
    private void addPaperTypeHot(String paperType, long addHotNumber) {
        SsExamPaperTypeHot ssExamPaperTypeHot = ssExamPaperTypeHotService.getByTypeValue(paperType);
        if (ssExamPaperTypeHot == null) {
            String typeName;
            if (ExamConsts.ExamType.CURRENCY.equals(paperType)) {
                typeName = "各类考试通用";
            } else {
                SsSysBas sysBas = ssSysBasService.getByBasTypeAndDictValue(SysBasConsts.EXAM_TYPE, paperType);
                if (sysBas == null) {
                    throw new ServiceException("题库分类错误");
                }
                typeName = sysBas.getDictName();
            }
            ssExamPaperTypeHot = new SsExamPaperTypeHot();
            ssExamPaperTypeHot.setTypeValue(paperType);
            ssExamPaperTypeHot.setTypeName(typeName);
            ssExamPaperTypeHot.setHotNumber(addHotNumber);
            ssExamPaperTypeHotService.insertSelective(ssExamPaperTypeHot);
        } else {
            ssExamPaperTypeHotService.incrementHotNumber(ssExamPaperTypeHot.getId(), addHotNumber);
        }
    }

    /**
     * 格式化年月  YYYY年M月
     *
     * @param belongYearMonth
     * @return
     */
    private String getFormatYearMonth(String belongYearMonth) {
        String yearStr = belongYearMonth.substring(0, 4);
        String monthStr = belongYearMonth.substring(5, 7);
        int month = Integer.parseInt(monthStr);
        return String.format("%s年%d月", yearStr, month);
    }

    /**
     * 生成按照月份分组的题目集
     *
     * @param belongYearMonth
     * @return
     */
    private String getMonthGroupQuestionTitle(String belongYearMonth) {
        boolean isDate = Pattern.matches("^[0-9]{4}-(([1][0-2])|([0][1-9])).*", belongYearMonth);
        if (isDate) {
            return String.format("%s时事政治", getFormatYearMonth(belongYearMonth));
        } else {
            return belongYearMonth;
        }
    }

    /**
     * 记录错题
     *
     * @param userId         用户id
     * @param ssExamQuestion 选择题对象
     */
    private void addErrorQuestion(String userId, SsExamQuestion ssExamQuestion) {
        SsExamErrorQuestion ssExamErrorQuestion = ssExamErrorQuestionService.getByUserIdWithQuestionId(userId, ssExamQuestion.getId());
        if (ssExamErrorQuestion == null) {
            String belongYearMonth = formatBelongYearMonth(ssExamQuestion.getBelongDate());
            ssExamErrorQuestion = new SsExamErrorQuestion();
            ssExamErrorQuestion.setBelongYearMonth(belongYearMonth);
            ssExamErrorQuestion.setQuestionId(ssExamQuestion.getId());
            ssExamErrorQuestion.setUserId(userId);
            ssExamErrorQuestionService.insert(ssExamErrorQuestion);
        }
    }

    /**
     * 同步试卷作答记录
     *
     * @param paperRecord
     * @param paperRecordInVO
     */
    private void synPaperRecords(SsExamPaperRecord paperRecord, PaperRecordInVO paperRecordInVO) {
        if (paperRecord == null) {
            throw new ServiceException("作答记录不存在");
        }

        if (ExamConsts.TestState.FINISH.equals(paperRecord.getTestState())) {
            throw new ServiceException("您已交卷");
        }
        paperRecord.setUserTime(paperRecordInVO.getUserTime());
        paperRecord.setFinishNumber(paperRecordInVO.getFinishNumber());
        List<PaperRecordInVO.PaperQuestionRecord> paperQuestionRecords = paperRecordInVO.getQuestions();
        for (PaperRecordInVO.PaperQuestionRecord record : paperQuestionRecords) {
            SsExamPaperQuestionRecord ssExamPaperQuestionRecord = ssExamPaperQuestionRecordService
                    .getByPaperRecordIdWithQuestionId(paperRecord.getId(), record.getQuestionId());
            if (ssExamPaperQuestionRecord == null) {
                ssExamPaperQuestionRecord = new SsExamPaperQuestionRecord();
                setSsExamPaperQuestionRecord(ssExamPaperQuestionRecord, paperRecord.getId(), record);
                ssExamPaperQuestionRecordService.insertSelective(ssExamPaperQuestionRecord);
            } else {
                setSsExamPaperQuestionRecord(ssExamPaperQuestionRecord, paperRecord.getId(), record);
                ssExamPaperQuestionRecordService.updateByPrimaryKeySelective(ssExamPaperQuestionRecord);
            }

        }
    }

    /**
     * 设值
     *
     * @param ssExamPaperQuestionRecord
     * @param paperRecordId
     * @param record
     */
    private void setSsExamPaperQuestionRecord(SsExamPaperQuestionRecord ssExamPaperQuestionRecord, Long paperRecordId, PaperRecordInVO.PaperQuestionRecord record) {
        if (ssExamPaperQuestionRecord.getId() == null) {
            ssExamPaperQuestionRecord.setPaperRecordId(paperRecordId);
            ssExamPaperQuestionRecord.setQuestionId(record.getQuestionId());
            ssExamPaperQuestionRecord.setSequence(record.getSequence());
        }
        List<Integer> userAnswers = record.getUserAnswers();
        ssExamPaperQuestionRecord.setUserAnswer(JSON.toJSONString(userAnswers));
    }

    /**
     * 刷新总题数，总阶段数
     * <p>
     * 1.目前方案是，倒二阶段，和最后一个阶段的时候触发更新<br>
     * 2.判断每天11点之后更新一次题库（2017年11月9日新增）
     * <p>
     * 。。。（思考如何在题库更新时才刷新）
     *
     * @param ssExamTestSchedule
     * @param testsOutVo
     */
    private void updateQuestionNumberAndStageNumber(SsExamTestSchedule ssExamTestSchedule, TestsOutVo testsOutVo) {
        int oldQuestionNumber = ssExamTestSchedule.getQuestionNumber();
        int oldStageNumber = ssExamTestSchedule.getStageNumber();
        int oldNowStage = ssExamTestSchedule.getNowStage();
        boolean isLastStage = oldNowStage >= oldStageNumber;
        if (oldNowStage == (oldStageNumber - 1) || isLastStage) {
            executeQuestionNumberAndStageNumber(ssExamTestSchedule, testsOutVo,
                    isLastStage, oldQuestionNumber, oldStageNumber);
            return;
        }

        //判断每天11点之后更新一次题库
        String todayDate = DateUtils.formatDate(new Date(System.currentTimeMillis()), "yyyyMMdd");
        String updateDate = String.format("%s110000", todayDate);
        boolean isUpdate = ssExamTestSchedule.getLastSynTime().compareTo(updateDate) < 0;
        if (isUpdate) {
            executeQuestionNumberAndStageNumber(ssExamTestSchedule, testsOutVo,
                    false, oldQuestionNumber, oldStageNumber);
        }
    }

    /**
     * 执行更新阶段数 和 题目数
     *
     * @param ssExamTestSchedule
     * @param testsOutVo
     * @param isLastStage
     * @param oldQuestionNumber
     * @param oldStageNumber
     */
    private void executeQuestionNumberAndStageNumber(SsExamTestSchedule ssExamTestSchedule,
                                                     TestsOutVo testsOutVo, boolean isLastStage,
                                                     int oldQuestionNumber, int oldStageNumber) {
        int questionNumber = getQuestionNumber(testsOutVo);
        int stageNumber = getStageNumber(questionNumber);
        if (isLastStage) {
            //判断最后一次练习是否有记录
            SsExamTestRecord ssExamTestRecord = ssExamTestRecordService
                    .getByStageAndScheduleId(oldStageNumber, ssExamTestSchedule.getId());
            if (ssExamTestRecord != null) {
                stageNumber = oldStageNumber + getStageNumber(questionNumber - oldQuestionNumber);
            }
        }
        ssExamTestSchedule.setQuestionNumber(questionNumber);
        ssExamTestSchedule.setStageNumber(stageNumber);
        ssExamTestSchedule.setLastSynTime(DateUtils.formatDate(new Date(System.currentTimeMillis()), "yyyyMMddHHmmss"));
        if (ssExamTestSchedule.getNowQuestion() > ssExamTestSchedule.getQuestionNumber()) {
            ssExamTestSchedule.setNowQuestion(ssExamTestSchedule.getQuestionNumber());
        }
        ssExamTestScheduleService.update(ssExamTestSchedule);
    }

    /**
     * 获取试卷选择题列表
     *
     * @param paperId            试卷id
     * @param ssExamPaperRecord  作答记录
     * @param freeQuestionNumber 免费题数
     * @return PaperRecordVO.PaperQuestion 试卷选择题列表
     */
    private List<PaperRecordOutVO.PaperQuestion> getPaperQuestions(
            Long paperId, SsExamPaperRecord ssExamPaperRecord, int freeQuestionNumber) {
        List<PaperRecordOutVO.PaperQuestion> questions = new ArrayList<>();
        List<SsExamPaperQuestion> ssExamPaperQuestions = ssExamPaperQuestionService
                .findByPaperId(paperId);
        List<SsExamPaperQuestionRecord> ssExamPaperQuestionRecords = ssExamPaperQuestionRecordService
                .findSequenceDescByRecordId(ssExamPaperRecord.getId());
        for (int i = 0; i < ssExamPaperQuestions.size(); i++) {
            SsExamPaperQuestion ssExamPaperQuestion = ssExamPaperQuestions.get(i);
            boolean authorization = false;
            int sequence = i + 1;
            if (freeQuestionNumber >= sequence) {
                authorization = true;
            }
            PaperRecordOutVO.PaperQuestion paperQuestion;
            if (authorization) {
                SsExamQuestion question = ssExamQuestionService.getById(ssExamPaperQuestion.getQuestionId());
                paperQuestion = getPaperQuestion(question, sequence, ssExamPaperQuestionRecords, true);
                paperQuestion.setIsCollection(getIsCollection(ssExamPaperRecord.getUserId(), question.getId()));
            } else {
                paperQuestion = new PaperRecordOutVO.PaperQuestion();
                paperQuestion.init();
                paperQuestion.setQuestionId(ssExamPaperQuestion.getQuestionId());
                paperQuestion.setAuthorization(false);
            }

            questions.add(paperQuestion);
        }
        return questions;
    }

    /**
     * 获取试卷选择题对象
     *
     * @param question
     * @param sequence
     * @param ssExamPaperQuestionRecords
     * @param authorization
     * @return
     */
    private PaperRecordOutVO.PaperQuestion getPaperQuestion(
            SsExamQuestion question,
            int sequence,
            List<SsExamPaperQuestionRecord> ssExamPaperQuestionRecords,
            boolean authorization) {
        PaperRecordOutVO.PaperQuestion paperQuestion = new PaperRecordOutVO.PaperQuestion(question, sequence, authorization);
        for (int i = ssExamPaperQuestionRecords.size() - 1; i >= 0; i--) {
            SsExamPaperQuestionRecord questionRecord = ssExamPaperQuestionRecords.get(i);
            if (questionRecord.getQuestionId().equals(question.getId())) {
                boolean isTrue = questionRecord.getIsTrue() == 1;
                paperQuestion.setIsTrue(isTrue);
                List<Integer> userAnswers = JSON.parseArray(questionRecord.getUserAnswer(), Integer.class);
                paperQuestion.setUserAnswers(userAnswers);
                ssExamPaperQuestionRecords.remove(i);
            }
        }
        paperQuestion.setOptions(getOptions(question.getId()));
        return paperQuestion;
    }

    /**
     * 设值
     *
     * @param userId
     * @param ssExamPaper
     * @param ssExamPaperRecord
     */
    private void setSsExamPaperRecord(String userId, SsExamPaper ssExamPaper, SsExamPaperRecord ssExamPaperRecord) {
        ssExamPaperRecord.setUserId(userId);
        ssExamPaperRecord.setPaperId(ssExamPaper.getId());
        ssExamPaperRecord.setTestState(ExamConsts.TestState.PROCESSING);
        ssExamPaperRecord.setPaperName(ssExamPaper.getPaperName());
        ssExamPaperRecord.setQuestionNumber(ssExamPaper.getQuestionNumber());
        ssExamPaperRecord.setFinishNumber(0);
        ssExamPaperRecord.setCorrectNumber(0);
        ssExamPaperRecord.setCoefficient(ssExamPaper.getCoefficient());
        ssExamPaperRecord.setCorrectAvgRate(BigDecimal.valueOf(0.00));
        ssExamPaperRecord.setBeatRate(BigDecimal.valueOf(0.00));
        ssExamPaperRecord.setUserTime(0);
        ssExamPaperRecord.setHistoryId(0L);
    }

    /**
     * 获取每套试卷的免费体验题数
     * 每套题库非VIP可以体验10%的题目，至少1题，最多5题
     *
     * @param questionNumber
     * @return
     */
    private int getFreeQuestionNumber(Integer questionNumber) {
        int minNumber = 1;
        int maxNumber = 5;
        int number = (int) (questionNumber * 0.1);
        if (number < 1) {
            number = minNumber;
        } else if (number > maxNumber) {
            number = maxNumber;
        }
        return number;
    }

    /**
     * 判断用户答题是否正确
     *
     * @param questionId 选择题id
     * @param userAnswer 用户的作答 json格式
     * @return
     */
    private boolean isTrue(Long questionId, String userAnswer) {
        boolean isTrue = false;
        int trueNumber = 0;
        int userTrueNumber = 0;
        List<Integer> userAnswers = JSON.parseArray(userAnswer, Integer.class);
        List<SsExamQuestionOption> options = ssExamQuestionOptionService.listByQuestionId(questionId);
        for (SsExamQuestionOption option : options) {
            boolean optionIsTrue = option.getIsTrue() == 1;
            if (optionIsTrue) {
                trueNumber++;
            }
            for (Integer answer : userAnswers) {
                boolean answerIsTrue = answer.compareTo(option.getSequence()) == 0 && optionIsTrue;
                if (answerIsTrue) {
                    userTrueNumber++;
                }
            }
        }
        if (trueNumber == userTrueNumber) {
            isTrue = true;
        }
        return isTrue;
    }

    /**
     * 同步作答记录
     *
     * @param ssExamTestRecord
     * @param testRecordsByIdInVo
     * @return
     */
    private void synTestRecords(SsExamTestRecord ssExamTestRecord, TestRecordsByIdInVo testRecordsByIdInVo) {
        if (ssExamTestRecord == null) {
            throw new ServiceException("作答记录不存在");
        }
        if (ExamConsts.TestState.FINISH.equals(ssExamTestRecord.getTestState())) {
            throw new ServiceException("您已交卷");
        }
        ssExamTestRecord.setUserTime(testRecordsByIdInVo.getUserTime());
        ssExamTestRecord.setFinishNumber(testRecordsByIdInVo.getFinishNumber());
        List<TestRecordsByIdInVo.TestQuestion> testQuestions = testRecordsByIdInVo.getQuestions();
        for (TestRecordsByIdInVo.TestQuestion testQuestion : testQuestions) {
            SsExamTestQuestion ssExamTestQuestion = ssExamTestQuestionService
                    .getById(testQuestion.getTestQuestionId());
            List<Integer> userAnswers = testQuestion.getUserAnswers();
            ssExamTestQuestion.setUserAnswer(JSON.toJSONString(userAnswers));
            ssExamTestQuestionService.updateByPrimaryKeySelective(ssExamTestQuestion);
        }
    }

    /**
     * 根据刷题计划，取未做的题目
     *
     * @param schedule
     * @return
     */
    private List<SsExamQuestion> queryNumByTestScheduleWithNotTest(SsExamTestSchedule schedule) {
        List<SsExamQuestion> ssExamQuestions;
        ExamInfoVO examInfoVO = new ExamInfoVO(schedule);
        String startDate = getStartDate(examInfoVO);
        String endDate = getEndDate(examInfoVO);
        String province = examInfoVO.getExamProvince();
        QuestionNotTestVO questionNotTestVO;
        int num = 20;
        switch (schedule.getExamType()) {
            case ExamConsts.ExamType.GUOKAO://国考
            case ExamConsts.ExamType.ZYLX://中央遴选
                questionNotTestVO = new QuestionNotTestVO();
                questionNotTestVO.setStartMonth(startDate);
                questionNotTestVO.setEndMonth(endDate);
                questionNotTestVO.setTestScheduleId(schedule.getId());
                questionNotTestVO.setNum(num);
                ssExamQuestions = ssExamQuestionService.queryByDomesticWithNotTest(questionNotTestVO);
                break;
            case ExamConsts.ExamType.SHENGKAO://省考
            case ExamConsts.ExamType.SYDW://事业单位
            case ExamConsts.ExamType.SZYF://三支一扶
            case ExamConsts.ExamType.JZSG:// 竞争上岗
            case ExamConsts.ExamType.JZG://军转干
            case ExamConsts.ExamType.DFLX:
                questionNotTestVO = new QuestionNotTestVO(startDate, endDate, province);
                questionNotTestVO.setTestScheduleId(schedule.getId());
                questionNotTestVO.setNum(num);
                ssExamQuestions = ssExamQuestionService.queryByDomesticAndTheProvinceWithNotTest(questionNotTestVO);
                break;
            case ExamConsts.ExamType.YHZK://银行招考
                List<String> topics = new ArrayList<>();
                topics.add(ExamConsts.Topic.ECONOMICS);
                topics.add(ExamConsts.Topic.SCIENCE_TECHNOLOGY);
                topics.add(ExamConsts.Topic.POLITICS);
                questionNotTestVO = new QuestionNotTestVO(startDate, endDate, province);
                questionNotTestVO.setTopics(topics);
                questionNotTestVO.setTestScheduleId(schedule.getId());
                questionNotTestVO.setNum(num);
                ssExamQuestions = ssExamQuestionService.queryByDomesticAndTheProvinceWithNotTest(questionNotTestVO);
                break;
            case ExamConsts.ExamType.KAOIYAN://考研
                questionNotTestVO = new QuestionNotTestVO();
                questionNotTestVO.setStartMonth(startDate);
                questionNotTestVO.setEndMonth(endDate);
                questionNotTestVO.setTestScheduleId(schedule.getId());
                questionNotTestVO.setNum(num);
                ssExamQuestions = ssExamQuestionService.queryByDomesticAndInternationalWithNotTest(questionNotTestVO);
                break;
            case ExamConsts.ExamType.ZHONGKAO://中考
            case ExamConsts.ExamType.GAOKAO://高考
            case ExamConsts.ExamType.QYZP://企业招聘
            case ExamConsts.ExamType.XDXP://选调选聘
            case ExamConsts.ExamType.JSZP://教师招聘
            case ExamConsts.ExamType.GQZP://国企招聘
            case ExamConsts.ExamType.ZFGJ://政法干警
            case ExamConsts.ExamType.QITA://其他
                questionNotTestVO = new QuestionNotTestVO(startDate, endDate, province);
                questionNotTestVO.setTestScheduleId(schedule.getId());
                questionNotTestVO.setNum(num);
                ssExamQuestions = ssExamQuestionService.queryByDomesticAndInternationalAndTheProvinceWithNotTest(questionNotTestVO);
                break;
            default:
                ssExamQuestions = new ArrayList<>();
                break;
        }
        return ssExamQuestions;
    }

    /**
     * 获取记录题目
     *
     * @param ssExamQuestion
     * @param testQuestion
     * @return
     */
    private TestRecordVo.ExamQuestion getExamQuestion(SsExamQuestion ssExamQuestion, SsExamTestQuestion testQuestion, String userId) {
        List<Option> options = getOptions(ssExamQuestion.getId());
        boolean isEmphasis = ssExamQuestion.getIsEmphasis() == 1;
        boolean isCard = ssExamQuestion.getIsCard() == 1;
        boolean isTrue = testQuestion.getIsTrue() == 1;
        List<Integer> userAnswers = JSON.parseArray(testQuestion.getUserAnswer(), Integer.class);
        boolean isCollection = getIsCollection(userId, testQuestion.getQuestionId());
        TestRecordVo.ExamQuestion examQuestion = new TestRecordVo.ExamQuestion();
        examQuestion.setTestQuestionId(testQuestion.getId());
        examQuestion.setQuestionId(testQuestion.getQuestionId());
        examQuestion.setSequence(testQuestion.getSequence());
        examQuestion.setIsTrue(isTrue);
        examQuestion.setQuestion(ssExamQuestion.getQuestion());
        examQuestion.setAnalysis(ssExamQuestion.getAnalysis());
        examQuestion.setQuestionType(ssExamQuestion.getQuestionType());
        examQuestion.setIsEmphasis(isEmphasis);
        examQuestion.setIsCard(isCard);
        examQuestion.setOptions(options);
        examQuestion.setUserAnswers(userAnswers);
        examQuestion.setIsCollection(isCollection);
        return examQuestion;
    }

    /**
     * 判断题目是否收藏
     *
     * @param userId
     * @param questionId
     * @return
     */
    private boolean getIsCollection(String userId, Long questionId) {
        SsExamCollectionQuestion ssExamCollectionQuestion = ssExamCollectionQuestionService
                .getByUserIdWithQuestionId(userId, questionId);
        return ssExamCollectionQuestion != null;
    }

    /**
     * 获取选项列表
     *
     * @param ssExamQuestionId
     * @return
     */
    public List<Option> getOptions(Long ssExamQuestionId) {
        List<Option> options = new ArrayList<>();
        List<SsExamQuestionOption> questionOptions = ssExamQuestionOptionService.listByQuestionId(ssExamQuestionId);
        for (SsExamQuestionOption questionOption : questionOptions) {
            boolean isTrue = questionOption.getIsTrue() == 1;
            Option option = new Option();
            option.setAnswer(questionOption.getAnswer());
            option.setIsTrue(isTrue);
            option.setSequence(questionOption.getSequence());
            options.add(option);
        }
        return options;
    }

    @Override
    public BaseQuestion getBaseQuestion(Long questionId) {
        SsExamQuestion ssExamQuestion = ssExamQuestionService.getById(questionId);
        BaseQuestion question = new BaseQuestion(ssExamQuestion);
        question.setOptions(this.getOptions(questionId));
        return question;
    }

    @Override
    public BaseQuestion getBaseQuestion(SsExamQuestion ssExamQuestion) {
        BaseQuestion question = new BaseQuestion(ssExamQuestion);
        question.setOptions(this.getOptions(ssExamQuestion.getId()));
        return question;
    }

    /**
     * 计算阶段数
     *
     * @return
     */
    private int getStageNumber(int questionNumber) {
        int stageNumber = questionNumber / 20;
        if (questionNumber % 20 != 0) {
            stageNumber++;
        }
        return stageNumber;
    }

    /**
     * 计算考试题数
     *
     * @param examInfoVO
     * @return
     */
    private int getQuestionNumber(ExamInfoVO examInfoVO) {
        int questionNumber;
        String startDate = getStartDate(examInfoVO);
        String endDate = getEndDate(examInfoVO);
        String province = examInfoVO.getExamProvince();
        QuestionCountVO questionCountVO;
        switch (examInfoVO.getExamType()) {
            case ExamConsts.ExamType.GUOKAO://国考
            case ExamConsts.ExamType.ZYLX://中央遴选
                questionCountVO = new QuestionCountVO();
                questionCountVO.setStartMonth(startDate);
                questionCountVO.setEndMonth(endDate);
                questionNumber = ssExamQuestionService.countByDomestic(questionCountVO);
                break;
            case ExamConsts.ExamType.SHENGKAO://省考
            case ExamConsts.ExamType.SYDW://事业单位
            case ExamConsts.ExamType.SZYF://三支一扶
            case ExamConsts.ExamType.JZSG:// 竞争上岗
            case ExamConsts.ExamType.JZG://军转干
            case ExamConsts.ExamType.DFLX:
                questionCountVO = new QuestionCountVO(startDate, endDate, province);
                questionNumber = ssExamQuestionService.countByDomesticAndTheProvince(questionCountVO);
                break;
            case ExamConsts.ExamType.YHZK://银行招考
                List<String> topics = new ArrayList<>();
                topics.add(ExamConsts.Topic.ECONOMICS);
                topics.add(ExamConsts.Topic.SCIENCE_TECHNOLOGY);
                topics.add(ExamConsts.Topic.POLITICS);
                questionCountVO = new QuestionCountVO(startDate, endDate, province);
                questionCountVO.setTopics(topics);
                questionNumber = ssExamQuestionService.countByDomesticAndTheProvince(questionCountVO);
                break;
            case ExamConsts.ExamType.KAOIYAN://考研
                questionCountVO = new QuestionCountVO();
                questionCountVO.setStartMonth(startDate);
                questionCountVO.setEndMonth(endDate);
                questionNumber = ssExamQuestionService.countByDomesticAndInternational(questionCountVO);
                break;
            case ExamConsts.ExamType.ZHONGKAO://中考
            case ExamConsts.ExamType.GAOKAO://高考
            case ExamConsts.ExamType.QYZP://企业招聘
            case ExamConsts.ExamType.XDXP://选调选聘
            case ExamConsts.ExamType.JSZP://教师招聘
            case ExamConsts.ExamType.GQZP://国企招聘
            case ExamConsts.ExamType.ZFGJ://政法干警
            case ExamConsts.ExamType.QITA://其他
                questionCountVO = new QuestionCountVO(startDate, endDate, province);
                questionNumber = ssExamQuestionService.countByDomesticAndInternationalAndTheProvince(questionCountVO);
                break;
            default:
                questionNumber = 0;
                break;
        }
        return questionNumber;
    }

    private String getEndDate(ExamInfoVO testsInVo) {
        String endDate = testsInVo.getExamDate();
        if (testsInVo.getExamDate().length() > 7) {
            endDate = testsInVo.getExamDate().substring(0, 7);
        }
        endDate = String.format("%s-31", endDate);
        return endDate;
    }

    /**
     * 获取开始时间
     *
     * @param examInfoVO
     * @return
     */
    private String getStartDate(ExamInfoVO examInfoVO) {
        SsSysBas ssSysBas = ssSysBasService.getByBasTypeAndDictValue(SysBasConsts.EXAM_RANGE, examInfoVO.getExamRange());
        int number = Integer.parseInt(ssSysBas.getAttr1());
        return calculateStartDate(examInfoVO, number);
    }

    /**
     * 计算开始日期
     *
     * @param examInfoVO
     * @param number
     * @return
     */
    private String calculateStartDate(ExamInfoVO examInfoVO, int number) {
        int year = Integer.parseInt(examInfoVO.getExamDate().substring(0, 4));
        int month = Integer.parseInt(examInfoVO.getExamDate().substring(5, 7));
        if (month - number < 0) {
            int years = (number - 12) / 12 + 1;
            year = year - years;
            month = (month + (12 * years)) - number;
        } else {
            month = month - number;
        }

        String monthStr = String.valueOf(month);
        if (monthStr.length() == 1) {
            monthStr = String.format("0%d", month);
        }
        return String.format("%d-%s-01", year, monthStr);
    }

    /**
     * 判断考试时间合法性
     *
     * @param examDate yyyy-MM
     */
    private void validExamDate(String examDate) {
        String years = examDate.substring(0, 4);
        String months = examDate.substring(5, 7);
        int compareToYears = years.compareTo(DateUtils.getYear());
        int compareToMonths = months.compareTo(DateUtils.getMonth());
        if (compareToYears == 0 && compareToMonths >= 0) {
            return;
        }

        if (compareToYears == 1 && compareToMonths <= 0) {
            return;
        }
        throw new ServiceException("考试时间只能选最近的12个月");
    }

    /**
     * 判断练习进度的状态
     *
     * @param ssExamTestSchedule
     * @return
     */
    private String getTestsStatus(SsExamTestSchedule ssExamTestSchedule) {
        String status = TestsOutVo.Status.NORMAL;
        //计划没有题的情况
        if (ssExamTestSchedule.getQuestionNumber() < 1) {
            status = TestsOutVo.Status.NOT_SUBJECT;
            return status;
        }
        //首次刷题
        if (ssExamTestSchedule.getNowStage() == 1) {
            SsExamTestRecord ssExamTestRecord = ssExamTestRecordService
                    .getByStageAndScheduleIdAndState(1, ssExamTestSchedule.getId(), ExamConsts.TestState.PROCESSING);
            if (ssExamTestRecord == null) {
                status = TestsOutVo.Status.FIRST;
                return status;
            }
        }
        //当是最后一个阶段的时候
        boolean isLastStage = ssExamTestSchedule.getNowStage()
                .compareTo(ssExamTestSchedule.getStageNumber()) >= 0;
        if (isLastStage) {
            //判断最后一次练习是否已交卷
            SsExamTestRecord ssExamTestRecord = ssExamTestRecordService
                    .getByStageAndScheduleIdAndState(ssExamTestSchedule.getStageNumber(),
                            ssExamTestSchedule.getId(), ExamConsts.TestState.FINISH);
            boolean isSubmit = false;
            if (ssExamTestRecord != null) {
                isSubmit = true;
            }
            if (isSubmit) {
                boolean isFinish = ssExamTestSchedule.getLastSynTime()
                        .compareTo(ssExamTestSchedule.getExamDate()) > 0;
                if (isFinish) {
                    status = TestsOutVo.Status.FINISH;
                } else {
                    status = TestsOutVo.Status.PENDING_UPDATE;
                }
            }

        }
        return status;
    }

    /**
     * 设置考试信息，转化为汉字
     *
     * @param examInfoVO
     */
    private void setExamInfoVO(ExamInfoVO examInfoVO) {
        SsSysBas sysBas;
        if (StringUtils.isNotBlank(examInfoVO.getExamType())) {
            sysBas = ssSysBasService.getByBasTypeAndDictValue(SysBasConsts.EXAM_TYPE, examInfoVO.getExamType());
            examInfoVO.setExamType(sysBas.getDictName());
        }
        if (StringUtils.isNotBlank(examInfoVO.getExamProvince())) {
            sysBas = ssSysBasService.getByBasTypeAndDictValue(SysBasConsts.PROVINCE, examInfoVO.getExamProvince());
            examInfoVO.setExamProvince(sysBas.getDictName());
        }
        if (StringUtils.isNotBlank(examInfoVO.getExamCity())) {
            sysBas = ssSysBasService.getByBasTypeAndDictValue(SysBasConsts.CITY, examInfoVO.getExamCity());
            examInfoVO.setExamCity(sysBas.getDictName());
        }
        if (StringUtils.isNotBlank(examInfoVO.getExamRange())) {
            sysBas = ssSysBasService.getByBasTypeAndDictValue(SysBasConsts.EXAM_RANGE, examInfoVO.getExamRange());
            examInfoVO.setExamRange(sysBas.getDictName());
        }
    }

    /**
     * 业务校验
     *
     * @param examInfoVO
     */
    private void validExamInfoVO(ExamInfoVO examInfoVO) {
        if (StringUtils.isNotBlank(examInfoVO.getExamType())) {
            SsSysBas sysBas = ssSysBasService.getByBasTypeAndDictValue(SysBasConsts.EXAM_TYPE, examInfoVO.getExamType());
            if (sysBas == null) {
                throw new ServiceException("考试类型错误");
            }
        }
        if (StringUtils.isNotBlank(examInfoVO.getExamProvince())) {
            SsSysBas sysBas = ssSysBasService.getByBasTypeAndDictValue(SysBasConsts.PROVINCE, examInfoVO.getExamProvince());
            if (sysBas == null) {
                throw new ServiceException("考试省份错误");
            }
        }
        if (StringUtils.isNotBlank(examInfoVO.getExamCity())) {
            SsSysBas sysBas = ssSysBasService.getByBasTypeAndDictValue(SysBasConsts.CITY, examInfoVO.getExamCity());
            if (sysBas == null) {
                throw new ServiceException("考试城市错误");
            }
        }
        if (StringUtils.isNotBlank(examInfoVO.getExamRange())) {
            SsSysBas sysBas = ssSysBasService.getByBasTypeAndDictValue(SysBasConsts.EXAM_RANGE, examInfoVO.getExamRange());
            if (sysBas == null) {
                throw new ServiceException("考试范围错误");
            }
        }
        if (StringUtils.isNotBlank(examInfoVO.getExamDate())) {
            validExamDate(examInfoVO.getExamDate());
        }

    }

    /**
     * 设值
     *
     * @param ssExamInfo
     * @param examInfoVO
     */
    private void setExamInfo(SsExamInfo ssExamInfo, ExamInfoVO examInfoVO) {
        if (StringUtils.isNotBlank(examInfoVO.getExamType())) {
            ssExamInfo.setExamType(examInfoVO.getExamType());
        }
        if (StringUtils.isNotBlank(examInfoVO.getExamProvince())) {
            ssExamInfo.setExamProvince(examInfoVO.getExamProvince());
        }
        if (StringUtils.isNotBlank(examInfoVO.getExamCity())) {
            ssExamInfo.setExamCity(examInfoVO.getExamCity());
        }
        if (StringUtils.isNotBlank(examInfoVO.getExamDate())) {
            ssExamInfo.setExamDate(examInfoVO.getExamDate());
        }
        if (StringUtils.isNotBlank(examInfoVO.getExamRange())) {
            ssExamInfo.setExamRange(examInfoVO.getExamRange());
        }
    }

    /**
     * 设置当前完成题目数
     *
     * @param testsOutVo
     * @param ssExamTestSchedule
     */
    private void setNowQuestion(TestsOutVo testsOutVo, SsExamTestSchedule ssExamTestSchedule) {
        SsExamTestRecord ssExamTestRecord = ssExamTestRecordService
                .getByStageAndScheduleId(ssExamTestSchedule.getNowStage(), ssExamTestSchedule.getId());
        if (ssExamTestRecord != null && ExamConsts.TestState.PROCESSING.equals(ssExamTestRecord.getTestState())) {
            int nowQuestion = testsOutVo.getNowQuestion() + ssExamTestRecord.getFinishNumber();
            if (nowQuestion > ssExamTestSchedule.getQuestionNumber()) {
                nowQuestion = ssExamTestSchedule.getQuestionNumber();
            }
            testsOutVo.setNowQuestion(nowQuestion);
        }
    }


    /**
     * 格式化分类
     *
     * @param belongDate
     * @return
     */
    private String formatBelongYearMonth(String belongDate) {
        boolean isDate = Pattern.matches("^[0-9]{4}-(([1][0-2])|([0][1-9])).*", belongDate);
        String belongYearMonth = belongDate;
        if (isDate) {
            belongYearMonth = belongDate.substring(0, 7);
        }
        return belongYearMonth;
    }
}
